from apps.Movie.models import Movies, Ratings, Relevancy
from apps.Index.models import Users
from django.shortcuts import render
from django.http import JsonResponse
import pandas as pd
import numpy as np
from pyspark.mllib.recommendation import ALS
from pyspark import SparkContext, SparkConf
from django.conf import settings
from sklearn.linear_model import LinearRegression
from collections import defaultdict


def cosine(l1, l2):
    fenzi = 0
    for i in range(len(l1)):
        fenzi += l1[i] * l2[i]
    return fenzi / (np.sqrt(sum([i ** 2 for i in l1])) * np.sqrt(sum([i ** 2 for i in l2])))


def transform_rantings(ratings):
    table = pd.DataFrame(columns=['userId', 'movieId', 'rating'], index=[])
    for i in range(len(ratings)):
        table.loc[i] = [int(ratings[i].userId.userId), int(ratings[i].movieId.movieId), ratings[i].rating]
    return table


def modelmake():
    userids = range(1, 100)
    ratings = Ratings.objects.filter(userId__in=userids)
    table = transform_rantings(ratings)
    dataset = [tuple([i for i in table.iloc[n]]) for n in range(len(table))]
    conf = SparkConf()
    try:
        sc = SparkContext(conf=conf)
        settings.SC = sc
    except:
        sc = settings.SC
    distData = sc.parallelize(dataset)
    model = ALS.train(distData, rank=1)
    genomepart = pd.read_csv(r'././apps/table.csv')
    settings.MODEL = model
    settings.genomepart = genomepart


def contentmake(request):
    username = request.session['phone']
    userid = Users.objects.filter(phone=username)[0].userId

    ratings = Ratings.objects.filter(userId=userid)
    table = transform_rantings(ratings)
    movies = table[:(len(table) // 10) * 7]

    dic = defaultdict()
    movieids = movies['movieId']
    rates = movies['rating']
    for i in range(len(movieids)):
        dic[movieids[i]] = rates[i]

    taglist = Relevancy.objects.filter(movieId__in=movieids)
    tagweight = defaultdict(float)
    for tag in taglist:
        tagweight[int(tag.tagId)] += tag.relevance * dic[tag.movieId.movieId]

    tagbest = [item[0] for item in sorted(tagweight.items(), key=lambda kv: (-kv[1], kv[0]))][:13]
    tagvector = np.array([item[1] for item in sorted(tagweight.items(), key=lambda kv: (-kv[1], kv[0]))])[:13]
    tagvector = (tagvector - tagvector.min()) / (tagvector.max() - tagvector.min()) - 0.5
    genomepart = settings.genomepart
    print([str(tag) for tag in tagbest])
    table = genomepart[[str(tag) for tag in tagbest]]
    l = []
    for i in range(len(table)):
        l.append((i, cosine(tagvector, table.loc[i])))
    return l


def statisticsmodel():
    avgrating = [rate.movieId.movieId for rate in Ratings.objects.raw(
        "SELECT ratingId, movieId FROM ratings group by movieId " +
        "having count(timestamp)>20 and avg(rating)>4 " +
        "order by avg(rating) desc")]
    newrating = [rate.movieId.movieId for rate in Ratings.objects.raw(
        "select ratingId, movieId, count(*) as co from (SELECT * FROM " +
        "ratings order by timestamp desc limit 1000) t1" +
        " group by movieId order by co desc, movieId")]
    return avgrating + newrating


def ALSmake(request):
    username = request.session['phone']
    userid = Users.objects.filter(phone=username)[0].userId
    sc = settings.SC
    model = settings.MODEL
    l = sc.parallelize([(userid, movie.movieId) for movie in Movies.objects.all()])
    modelresult = [(rate.product, rate.rating) for rate in model.predictAll(l).collect()]
    return modelresult


def recommend(request):
    if request.method == "GET":
        return render(request, 'Recommend/recommend.html')
    else:
        try:
            recommendlist = settings.recommendlist
        except:
            contentresult = contentmake(request)
            modelresult = ALSmake(request)

            dicmix = defaultdict(int)
            for i in modelresult:
                dicmix[i[0]] = i[1] * 0.42
            for i in contentresult:
                dicmix[i[0]] = i[1] * (1 - 0.42)
            mix = [i for i in sorted(dicmix.items(), key=lambda kv: (-kv[1], kv[0]))][:40]

            mixlist = [i[0] for i in mix]
            staticresult = statisticsmodel()[:10]

            recommendlist = mixlist + staticresult
            settings.recommendlist = recommendlist

        result = Movies.objects.filter(movieId__in=recommendlist)
        movieList = []
        for movie in result:
            movie = movie.getJsonObj()
            movieList.append(movie)
        movie_res = {
            'rows': movieList,
            'total': len(movieList)
        }
        return JsonResponse(movie_res, json_dumps_params={'ensure_ascii': False})
